from socket import *
from threading import Thread
from functools import wraps
from datetime import datetime
import logging
import time
import pymongo
import re
import api

def out_logger(flag):
    def logger(func):
        @wraps(func)
        def inner(self,*args, **kwargs):
            if 'msgtype' == flag:
                logging.info('接收到新消息')
                logging.info(self.msgType)
                logging.info((*args,*kwargs))
            elif 'db' == flag:
                logging.info('插入数据库')
            ret = func(self,*args, **kwargs)
            return ret
        return inner
    return logger

#log = None
def init_log():
    global log
    now = datetime.now().strftime('%F')
    fname = './log/' + now + '.txt'
    logging.basicConfig(level=logging.INFO,
                        filename=fname,
                        filemode='a+',
                        format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s-%(name)s: %(message)s')
    # log = logging.getLogger('danmu')
    # h1 = logging.FileHandler(fname)
    # h1.setLevel(logging.WARNING)
    # log.addHandler(h1)

class Danmu(object):
    def __init__(self):
        config = api.Config()
        self.roomid = int(config.get('douyu', 'ROOMID'))
        self.host = gethostbyname(config.get('douyu', 'HOST'))
        self.port = int(config.get('douyu', 'PORT'))
        self.codeLocalToServer = int(config.get('douyu', 'codeLocalToServer'))
        self.gid = int(config.get('douyu', 'gid'))
        self.mongo_url = config.get('db', 'MONGO_URL')
        self.mongo_db = config.get('db', 'MONGO_DB')
        self.mongo_set_danmu = config.get('db', 'MONGO_SET_DANMU')
        self.mongo_set_gift = config.get('db', 'MONGO_SET_GIFT')
        self.mongo_set_noble = config.get('db','MONGO_SET_NOBLE')
        self.myset_danmu = None
        self.myset_gift = None
        self.myset_noble = None
        self.sock = None
        self.msgType = ''


    def init_db_sock(self):
        client = pymongo.MongoClient(self.mongo_url)
        db = client[self.mongo_db]
        self.myset_danmu = db[self.mongo_set_danmu]
        self.myset_gift = db[self.mongo_set_gift]
        self.myset_noble = db[self.mongo_set_noble]


        self.sock = socket(AF_INET,SOCK_STREAM)
        self.sock.connect((self.host,self.port))

    @out_logger(flag='db')
    def save_to_mongo(self,myset,info):
        if myset.insert(info):
            print(info, '插入成功')
        else:
            print(info, '插入失败')

    def get_req(self,req):
        if 'login' == req:#登录
            ret = 'type@=loginreq/roomid@=%d/\0'%self.roomid
        elif 'joingroup' == req:#加入分组
            ret = 'type@=joingroup/rid@=%d/gid@=%d/\0'%(self.roomid,self.gid)
        elif 'logout' == req:#退出服务器
            ret = 'type@=logout/\0'
        elif 'keeplive' == req:#保持与服务器心跳连接
            ret = 'type@=mrkl/\0'
        else:
            print('保留请求')
            ret = 'type@=logout/\0'
        return ret

    def send_msg(self,msg):
        msg = msg.encode('utf-8')
        data_length = len(msg) + 8
        msgHead = data_length.to_bytes(4, 'little') + \
                  data_length.to_bytes(4, 'little') + \
                  self.codeLocalToServer.to_bytes(4, 'little')
        self.sock.send(msgHead)
        sent = 0
        while sent < len(msg):
            n = self.sock.send(msg[sent:])
            sent += n
    def login(self):
        self.init_db_sock()
        login = self.get_req('login')
        self.send_msg(login)
        #data = self.sock.recv(1024)
        print('login')

    def join_group(self):
        join = self.get_req('joingroup')
        self.send_msg(join)
        #data = self.sock.recv(1024)
    def logout(self):
        msg = self.get_req('logout')
        self.send_msg(msg)
        print('退出服务器')


    def deal_danmu(self):
        while True:
            data = self.sock.recv(1024)
            msgType = re.search(b'type@=(\w*)/',data)
            if not msgType:
                continue
            msgType = msgType.group(1)
            self.msgType = msgType
            self.deal_msg(msgType,data)

    @out_logger(flag='msgtype')
    def deal_msg(self,msgType,data):
        if b'chatmsg' == msgType:  # 弹幕
            try:
                info = {}
                danmu = re.search(b'nn@=(.*)/txt@=(.*?)/', data)
                if not danmu:
                    return
                info['name'] = danmu.group(1).decode()
                info['content'] = danmu.group(2).decode()
                self.save_to_mongo(self.myset_danmu, info)
            except UnicodeDecodeError:
                pass
        elif b'spbc' == msgType:  # 礼物信息
            try:
                info = {}
                info['roomid'] = re.search(b'rid@=(.*?)/', data).group(1).decode()  # 房间id
                info['sn'] = re.search(b'sn@=(.*?)/', data).group(1).decode()  # 赠送者昵称
                info['dn'] = re.search(b'dn@=(.*?)/', data).group(1).decode()  # 受赠者昵称
                info['gn'] = re.search(b'gn@=(.*?)/', data).group(1).decode()  # 礼物名称
                info['gc'] = re.search(b'gc@=(.*?)/', data).group(1).decode()  # 礼物数量
                info['drid'] = re.search(b'drid@=(.*?)/', data).group(1).decode()  # 赠送房间
                info['es'] = re.search(b'es@=(.*?)/', data).group(1).decode()  # 广播展示样式
                info['bgl'] = re.search(b'bgl@=(.*?)/', data).group(1).decode()  # 广播礼物类型

                self.save_to_mongo(self.myset_gift, info)
            except UnicodeDecodeError:
                pass
            except AttributeError:
                pass
        elif b'al' == msgType:  # 中途离开
            print('主播离开')
            self.logout()
        elif b'ab' == msgType:  # 返回
            print('主播返回')
            self.login()
            self.join_group()
        elif b'online_noble_list' == msgType:  # 贵族列表
            try:
                roomid = re.search(b'rid@=(.*?)/', data).group(1).decode()  # 房间id
                noble = re.search(b'nl@=(.*)', data).group(1).decode()
                # 'uid@AA=14207472@ASnn@AA=凉宫阵铁吴@ASicon@AA=avatar_v3@AAS201807@AAS2c9ee3674ba446f8b004d823e5b257cb@ASne@AA=4@ASlv@AA=64@ASrk@AA=44@ASpg@AA=1@ASrg@AA=1@ASsahf@AA=0@AS@
                uidList = re.findall(r'uid@AA=(.*?)@', noble)  # 用户id
                nnList = re.findall(r'ASnn@AA=(.*?)@', noble)  # 用户昵称
                neList = re.findall(r'ASne@AA=(.*?)@', noble)  #
                lvList = re.findall(r'ASlv@AA=(.*?)@', noble)  # 用户等级
                rkList = re.findall(r'ASrk@AA=(.*?)@', noble)  # 用户排名
                pgList = re.findall(r'ASpg@AA=(.*?)@', noble)  # 用户平台权限组
                rgList = re.findall(r'ASrg@AA=(.*?)@', noble)  # 用户房间权限组
                for person in zip(uidList, nnList, neList, lvList, rkList, pgList, rgList):
                    info = {}
                    info['roomid'] = roomid
                    info['uid'] = person[0]
                    info['nn'] = person[1]
                    info['ne'] = person[2]
                    info['lv'] = person[3]
                    info['rk'] = person[4]
                    info['pg'] = person[5]
                    info['rg'] = person[6]
                    if self.myset_noble.find_one({'uid': info['uid']}) is None:
                        self.save_to_mongo(self.myset_noble, info)
            except UnicodeDecodeError:
                pass
            except AttributeError:
                pass


    def keep_live(self):
        while True:
            keep = self.get_req('keeplive')
            self.send_msg(keep)
            time.sleep(30)


if __name__ == '__main__':
    init_log()
    danmu = Danmu()
    danmu.login()#登录
    danmu.join_group()#加入分组
    t1 = Thread(target=danmu.keep_live)
    t1.start()
    danmu.deal_danmu()#处理服务器消息
    t1.join()